//
//  SwiftMaker.swift
//  SwiftCodableBuilder
//
//  Created by Caijinglong on 2017/11/22.
//  Copyright © 2017年 kikt. All rights reserved.
//

import Foundation

class SwiftMakerHelper{

    class func make(jsonStr: String, objName: String, type: Int = 0) -> String {
        let maker = SwiftMaker(jsonStr: jsonStr, name: objName)
        return maker.make(type:type)
    }
    
}

private class SwiftMaker{
    
    var jsonStr = """
{"msg":"success","code":23,"desc":{"compony":"abc","level":1},"memberList":[{"name":"tommy","age":18},{"name":"jack","age":19}]}
"""

    private var name = ""

    private var classList = [Class]()

    init(jsonStr: String, name: String) {
        self.jsonStr = jsonStr
        self.name = name
    }

    func make(type:Int) -> String{
        let dict = getDictionaryFromJSONString(jsonString: jsonStr)
        makeObject(dict, name: name)
        classList.reverse()
//        NSLog("\(classList)")
        var result = ""
        classList.forEach { (c) in
            result.append(c.toString(type:type))
            result.append("\n\n")
        }
        return result
    }


    func makeObject(_ dict: Dictionary<String, Any>, name: String) {
        var clazz = Class(name: name)

        dict.forEach { (key, value) in
            
            if value is String {
                
                clazz.fieldList.append(Field(name: key, typeName: "String", defaultValue: "\"\""))
            } else if value is Int {
                
                clazz.fieldList.append(Field(name: key, typeName: "Int", defaultValue: "0"))
            } else if value is Dictionary<String, Any> {
                
                clazz.fieldList.append(Field(name: key, typeName: key, defaultValue: "\(key.upperFirst())()"))
                makeObject(value as! Dictionary<String, Any>, name: key)
            } else if value is Array<Any> {
                
                let result = makeList(list: value as! Array<Any>, name: key)
                let field: Field = Field(name: key, typeName: "[\(result.upperFirst())]", defaultValue: "[]")
                clazz.fieldList.append(field)
            }
        }
        classList.append(clazz)
    }

    func makeList(list: Array<Any>, name: String) -> String {
        if list.isEmpty {
            return "None"
        }

        let obj = list[0]
        if obj is String {
            return "String"
        } else if obj is Int {
            return "Int"
        } else if obj is Dictionary<String, Any> {
            makeObject(obj as! Dictionary<String, Any>, name: name)
            return name
        } else if obj is Array<Any> {
            let result = makeList(list: obj as! Array, name: name)
            return result
        }

        return "None"
    }
    
    func getDictionaryFromJSONString(jsonString:String) ->[String:Any]{
        let jsonData:Data = jsonString.data(using: .utf8)!
        
        let dict = try? JSONSerialization.jsonObject(with: jsonData, options: .mutableContainers)
        if dict != nil {
            return dict as! [String : Any]
        }
        return [String:Any]()
    }
}

private struct Field{
    var name:String
    var typeName:String
    var defaultValue:String
    
    func toString(type:Int) -> String {
        var result = "    var \(name): \(typeName.upperFirst())"
//        if(type == 0){
            result.append(" = \(defaultValue)")
//        }
        return result
    }
}

private struct Class {
    var name: String

    init(name: String) {
        self.name = name
    }

    var fieldList = [Field]()
    
    func toString(type:Int) -> String{
        var result = ""
        if(type == 0){
            result.append("struct")
        }else{
            result.append("class")
        }
        result.append(" \(self.name.upperFirst()) : Codable {\n")
        
        result.append("    init(){\n    }")
        
        fieldList.forEach { (f) in
            result.append("\n\(f.toString(type: type))")
        }
        
        result.append("\n\n}")
        return result
    }
}

extension String{
    func upperFirst()->String{
        let suf = String(self.suffix(from: index(startIndex, offsetBy: 1)))
        return "\(String.init(self[self.startIndex]).uppercased())\(suf)"
    }
}
